﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.IO;
using OpenLS.Spreadsheet.Expressions;

namespace OpenLS.Spreadsheet.Formats.Biff.Records
{
    internal class AutoFilterRecord : BiffRecord
    {
        private bool wJoin;
        private ushort iEntry;
        private AFDOper doper1;
        private AFDOper doper2;
        private XLUnicodeStringNoCch str1;
        private XLUnicodeStringNoCch str2;
        private bool fSimple1;
        private bool fTopN;
        private bool fSimple2;
        private bool fTop;
        private bool fPercent;
        private int wTopN;

        public static  IEnumerable<BiffRecord> GetAutoFilterRecord(FilterColumn column)
        {
            Contract.Assert(column != null);
            var filter = column.Filter;
                yield break;
            if (filter is CustomFilters)
            {
                AutoFilterRecord result = new AutoFilterRecord();
                result.iEntry = (ushort)(column.Column.Index - column.AutoFilter.Range.TopLeftCell.Column.Index);
                CustomFilters customFilters = (CustomFilters)filter;
                string s;
                result.doper1 = new AFDOper(customFilters.First, out s);
                if (s != null)
                    result.str1 = (XLUnicodeStringNoCch) s;
                result.doper2 = new AFDOper(customFilters.Second, out s);
                if (s != null)
                    result.str2 = (XLUnicodeStringNoCch) s;
                result.wJoin = !customFilters.And;
                yield return result;
                yield break;
            }
            if (filter is Top10Filter)
            {
                AutoFilterRecord result = new AutoFilterRecord
                                              {
                                                  iEntry =
                                                      (ushort)
                                                      (column.Column.Index -
                                                       column.AutoFilter.Range.TopLeftCell.Column.Index)
                                              };
                Top10Filter top10Filter = (Top10Filter)filter;
                result.fTopN = true;
                result.fTop = top10Filter.Top;
                result.fPercent = top10Filter.Percent;
                result.wTopN = (int)top10Filter.Value;
                yield return result;
                yield break;
            }
            if (filter is ValuesFilter)
            {
                yield return new AutoFilterRecord();
                ValuesFilter valuesFilter = (ValuesFilter) filter;

                AutoFilter12Record autoFilter12Record = new AutoFilter12Record(column,  valuesFilter);
                yield return autoFilter12Record;
                foreach (var v in valuesFilter.Filters)
                {
                    AF12Criteria c = new AF12Criteria(v);
                    yield return new ContinueFrt12Record(c.Write, column.Range.Address);
                }
                foreach (var v in valuesFilter.DateGroupItems)
                {
                    yield return new ContinueFrt12Record(null, column.Range.Address);
                }
                yield break;
            }
                if (filter is DynamicFilter)
                {
                    DynamicFilter dynamicFilter = (DynamicFilter) filter;
                    yield return new AutoFilterRecord();
                    foreach (var r in AutoFilter12Record.GetRecords(column, dynamicFilter))
                        yield return r;
                    yield break;
                }

                if (filter is IconFilter)
                    throw new NotImplementedException();
                if (filter is ColorFilter)
                    throw new NotImplementedException();
                else
                {
                    Debug.WriteLine(filter);
                    throw new NotImplementedException(filter.ToString());
                }
        }

        public AutoFilterRecord()
        {
        }


        protected override void ReadContent(BiffReaderContext context, int length)
        {
#if DEBUG
            var pos = context.BaseStream.Position;

#endif
            ReadReader(context.Reader);
            /*
            iEntry = context.ReadUInt16();
            var abcdeFwTopN = context.ReadUInt16();
            this.wJoin = (abcdeFwTopN & 0x3) == 1;
            this.fSimple1 = (abcdeFwTopN & 0x4) != 0;
            this.fSimple2 = (abcdeFwTopN & 0x8) != 0;
            this.fTopN = (abcdeFwTopN & 0x10) != 0;
            this.fTop = (abcdeFwTopN & 0x20) != 0;
            this.fPercent = (abcdeFwTopN & 0x40) != 0;
            this.wTopN = (abcdeFwTopN) >> 7;
            Contract.Assert(abcdeFwTopN == this.ABCDEFwTopN);
            this.doper1.Read(context.Reader);
            this.doper2.Read(context.Reader);
            if (doper1.vt == AFDOper.VtValueType.AFDOperStr)
            {
                AFDOper.AFDOperStr s = (AFDOper.AFDOperStr) doper1.vtValue;
                this.str1.Read(context.Reader, s.cch);
            }
            if (doper2.vt == AFDOper.VtValueType.AFDOperStr)
            {
                AFDOper.AFDOperStr s = (AFDOper.AFDOperStr) doper1.vtValue;
                this.str2.Read(context.Reader, s.cch);
            }*/
#if DEBUG
            Contract.Assert(context.BaseStream.Position == pos + length);

#endif
        }
        internal void ReadReader(BinaryReader context)
        {
#if DEBUG
            var pos = context.BaseStream.Position;

#endif
            iEntry = context.ReadUInt16();
            var abcdeFwTopN = context.ReadUInt16();
            this.wJoin = (abcdeFwTopN & 0x3) == 1;
            this.fSimple1 = (abcdeFwTopN & 0x4) != 0;
            this.fSimple2 = (abcdeFwTopN & 0x8) != 0;
            this.fTopN = (abcdeFwTopN & 0x10) != 0;
            this.fTop = (abcdeFwTopN & 0x20) != 0;
            this.fPercent = (abcdeFwTopN & 0x40) != 0;
            this.wTopN = (abcdeFwTopN) >> 7;
            Contract.Assert(abcdeFwTopN == this.ABCDEFwTopN);
            this.doper1.Read(context);
            this.doper2.Read(context);
            if (doper1.vt == AFDOper.VtValueType.AFDOperStr)
            {
                AFDOper.AFDOperStr s = (AFDOper.AFDOperStr)doper1.vtValue;
                this.str1.Read(context, s.cch);
            }
            if (doper2.vt == AFDOper.VtValueType.AFDOperStr)
            {
                AFDOper.AFDOperStr s = (AFDOper.AFDOperStr)doper1.vtValue;
                this.str2.Read(context, s.cch);
            }
        }

        internal override BiffRecordType RecordType
        {
            get { return BiffRecordType.AutoFilter; }
        }


        

        internal override void WriteContent(BiffWriterWorkbookContext context)
        {
            context.Write(iEntry);
            context.Write(this.ABCDEFwTopN);
            this.doper1.Write(context.Writer);
            this.doper2.Write(context.Writer);
            if (doper1.vt == AFDOper.VtValueType.AFDOperStr)
            {
                AFDOper.AFDOperStr s = (AFDOper.AFDOperStr)doper1.vtValue;
                Contract.Assert(((string) str1).Length == s.cch);
                this.str1.Write(context.Writer);
            }
            if (doper2.vt == AFDOper.VtValueType.AFDOperStr)
            {
                AFDOper.AFDOperStr s = (AFDOper.AFDOperStr)doper1.vtValue;
                Contract.Assert(((string)str2).Length == s.cch);
                this.str2.Write(context.Writer);
            }
        }

        protected ushort ABCDEFwTopN
        {
            get {
                ushort result = 0;
                if (wJoin)
                    result |= 0x01;
                if (fSimple1)
                    result |= 0x04;
                if (fSimple2)
                    result |= 0x08;
                if (fTopN)
                    result |= 0x10;
                if (fTop)
                    result |= 0x20;
                if (fPercent)
                    result |= 0x40;
                result |= (ushort)(wTopN << 7);
                return result;
            }
        }

        public FilterColumn GetColumn(AutoFilter autoFilter, Range range)
        {
            var columnIndex = range.TopLeftCell.Column.Index + this.iEntry;
            FilterColumn result = new FilterColumn(autoFilter, range, range.Worksheet.Columns[columnIndex]);
            result.Filter = GetFilter();
            return result;
        }

        private Filter GetFilter()
        {
            if (fTopN)
            {
                Top10Filter result = new Top10Filter();
                result.Percent = fPercent;
                result.Top = fTop;
                result.Value = wTopN;
                return result;

            }
            {
                if (Next is AutoFilter12Record)
                {
                    AutoFilter12Record autoFilter12Record = (AutoFilter12Record) Next;
                    return autoFilter12Record.GetFilter();
                }
                CustomFilters result = new CustomFilters();
                result.And = !this.wJoin;
                result.First = this.doper1.ToCustomFilter((string) this.str1);
                result.Second = this.doper2.ToCustomFilter((string) this.str2);
                return result;
            }
            throw new NotImplementedException();
        }
    }
}